/*
 * Copyright (C) 2009 Rafael Fernandes
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.phonebooksharing.android.activities.postcard;

import java.util.ArrayList;
import java.util.StringTokenizer;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.Window;
import android.view.ViewGroup.LayoutParams;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.ImageView.ScaleType;

import com.phonebooksharing.android.R;
import com.phonebooksharing.android.util.Global;
import com.phonebooksharing.android.widgets.drawable.FastBitmapDrawable;

import dalvik.system.VMRuntime;

/** 
 * 
 * @author Google Inc. (Lolcat Builder)
 * @author Rafael Fernandes (Re-wrote to be accurate with postcard functionality)
 *
 */
@SuppressWarnings("unchecked")
public class PostcardEditBackActivity extends Activity {
//    private static final String  TAG      = "PostCardView";

    private final PostCardObject stamp    = new PostCardObject(),
                                 headline = new PostCardObject(),
                                 message  = new PostCardObject(),
//                                 myInfo   = new PostCardObject(),
                                 address  = new PostCardObject();

    private PostCardView         iv;
    private PostCardObject       pcBeingDragged;
    
    private static final long INITIAL_HEAP_SIZE = 4*1024*1024;
    private long oldHeapSize;
    
    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        
        // Eliminate extra GCs during startup by setting the initial heap size to 4MB.
        oldHeapSize = VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
        
        requestWindowFeature(Window.FEATURE_NO_TITLE);
//        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        iv = new PostCardView(this);
        iv.setPostCard(R.drawable.postcard_bg);
        iv.setScaleType(ScaleType.FIT_XY);
        
        setStamp(BitmapFactory.decodeResource(getResources(), R.drawable.stamp));
        setHeadline("TITLE", 20, Color.BLACK);
        setMessage("Enter your big text here..." , 16, Color.BLACK);
        setAddress("To:\n\nTo whom\n\n\nBy:\n\n", 23, Color.BLACK);
        
        iv.renderScreen();
        setContentView(iv, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT));
    }
    
    @Override
    public void finish() {
        Global.setPostcard(null, new FastBitmapDrawable(iv.mWorkingBitmap));
        setResult(RESULT_OK);
        
//        iv.clear();
//        iv = null;
        super.finish();
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        VMRuntime.getRuntime().setMinimumHeapSize(oldHeapSize);
    }

    public void setStamp(Bitmap s) {
        final PostCardObject pc = stamp; 
        if(s == null) {
            //TODO: put a placeholder here
        } else {
            pc.object = s;
            pc.x = 326;
            pc.y = 12;
            pc.boundingBox = null;
            iv.addAvailableObjects(pc);
        }
    }

    public void setHeadline(String s, int fsize, int color) {
        final PostCardObject pc = headline;
        if(!TextUtils.isEmpty(s)) {
            pc.object = getMsg(s);
            pc.boundingBox = null;
            pc.x = 8;
            pc.y = 35;

            Paint p = new Paint();
            p.setAntiAlias(true);
            p.setTextSize(fsize);
            p.setColor(color);//RF
            pc.paint = p;
            
            iv.addAvailableObjects(pc);
        }
    }
    
    public void setMessage(String s, int fsize, int color) {
        final PostCardObject pc = message;
        if(!TextUtils.isEmpty(s)) {
            pc.object = getMsg(s);
            pc.boundingBox = null;
            pc.x = 8;
            pc.y = 90;
            
            Paint p = new Paint();
            p.setAntiAlias(true);
            p.setTextSize(fsize);
            p.setColor(color);//RF
            
            pc.paint = p;
            iv.addAvailableObjects(pc);
        }
    }
    
//    public void setMyInfo(Bitmap s) {
//        if(s == null) {
//        } else {
//            myInfo.object = s;
//            myInfo.boundingBox = null;
//            iv.addAvailableObjects(myInfo);
//        }
//    }
    
    public void setAddress(String s, int fsize, int color) {
        final PostCardObject pc = address;
        if(!TextUtils.isEmpty(s)) {
            pc.object = getMsg(s);
            pc.boundingBox = null;
            pc.x = 293;
            pc.y = 146;
            
            Paint p = new Paint();
            p.setAntiAlias(true);
            p.setTextSize(fsize);
            p.setColor(color);//RF
            pc.paint = p;
            iv.addAvailableObjects(pc);
        }
    }
    
    protected static ArrayList<String> getMsg(String d) {
        final ArrayList<String> l = new ArrayList<String>();
        final StringTokenizer st = new StringTokenizer(d, " \n", true);
        final int max=35;
        String x="";
        while (st.hasMoreElements()) {
            String c = (String)st.nextElement();
            
            if(x.length() + c.length() > max) {
//                Log.d(TAG, "x = " + x + " , c = " + c);
                l.add(x.trim());
                x = c.equals("\n") ? "" : c;
            } else {
                if(c.equals("\n")) {
//                    Log.d(TAG, "x = " + x + " , c = " + c);
                    l.add(x.trim());
                    x="";
                } else {
                    x += c;
                }
            }
        }
        if(x.length() > 0) l.add(x);

        return l;
    }
    
    protected void change(PostCardObject pc) {
        if(pc.object instanceof Bitmap) {
            //TODO: ask if user wants to have the picture from a list
            //or want to take from his pictures
        } else {
            showDialog(CHANGE_TEXT_DIALOG);
        }
    }
    
    private static final int CHANGE_TEXT_DIALOG = 0;
    
//    @Override
//    public boolean onCreateOptionsMenu(Menu menu) {
//        menu.add(0, 0, 0, "Change Text");
//        return true;
//    }
    
//    @Override
//    public boolean onOptionsItemSelected(MenuItem item) {
//        switch (item.getItemId()) {
//            case 0: {
//                showDialog(CHANGE_TEXT_DIALOG);
//                return true;
//            }
//            default: {
//                if (!item.hasSubMenu()) {
//                    Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show();
//                    return true;
//                }
//                break;
//            }
//        }
//        return false;
//    }
    
    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        switch (id) {
            case CHANGE_TEXT_DIALOG: {
                EditText tv = (EditText)((AlertDialog)dialog).findViewById(0);
                String s="";
                ArrayList<String> l = (ArrayList<String>)pcBeingDragged.object;
                for(int i = 0; i < l.size(); i++) {
                    s += l.get(i);
                    s += "\n";
                }
                tv.setText(s.substring(0, s.length()-1));//removing the last \n char
                break;
            }
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case CHANGE_TEXT_DIALOG: {
                EditText tv = new EditText(this);
                tv.setId(0);
                
                ScrollView sv = new ScrollView(this);
                sv.addView(tv);
                return new AlertDialog.Builder(this)
                    .setTitle("Change it")
                    .setView(sv)
                    .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            /* User clicked OK so do some stuff */
                            Editable e = ((EditText)((AlertDialog)dialog).findViewById(0)).getText();
                            pcBeingDragged.object = getMsg(e.toString());
                            pcBeingDragged.boundingBox = null;//needs to recreate the box's params
                            iv.renderScreen();
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {

                            /* User clicked cancel so do some stuff */
                        }
                    })
                    .create();
            }
            default:
                break;
        }
        return null;
    }
    
    class PostCardView extends ImageView {
        private Bitmap mScaledBitmap;  // The photo picked by the user, scaled-down
        private Bitmap mWorkingBitmap;  // The Bitmap we render the caption text into
        
        private final ArrayList<PostCardObject> availableObjects = new ArrayList<PostCardObject>();

        // State used while dragging a caption around
        private boolean mDragging;
        private int mTouchDownX, mTouchDownY;
        private final Rect mInitialDragBox = new Rect();
        private final Rect mCurrentDragBox = new Rect();
        private final RectF mCurrentDragBoxF = new RectF();  // used in onDraw()
        private final RectF mTransformedDragBoxF = new RectF();  // used in onDraw()

        public PostCardView(Context context) {
            super(context);
        }

        public PostCardView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public PostCardView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
        
        public void addAvailableObjects(PostCardObject pc) {
            availableObjects.add(pc);
        }

        /**
         * @return true if the user has set caption(s) for this LolcatView.
         */
        public boolean hasValidObjects() {
            return availableObjects.size() > 0;
        }

        public void clear() {
            mScaledBitmap = null;
            mWorkingBitmap = null;
            setImageDrawable(null);
        }
        
        public void setPostCard(int i) {
            setImageBitmap(BitmapFactory.decodeResource(getResources(), i));
            // Stash away a scaled-down bitmap.
            BitmapDrawable drawable = (BitmapDrawable) getDrawable();
            mScaledBitmap = drawable.getBitmap();
        }
        
        public void renderScreen() {
            if (mScaledBitmap == null) return;

            mWorkingBitmap = mScaledBitmap.copy(Bitmap.Config.ARGB_8888, true /* isMutable */);
            Canvas canvas = new Canvas(mWorkingBitmap);
            
            final ArrayList<PostCardObject> l = availableObjects;
            int c = l.size();
            for(int i=0; i<c; i++) {
                PostCardObject pc = l.get(i);
                if(pc.object instanceof Bitmap) {
                    writeBitmap(canvas, pc);
                } else {
                    writeString(canvas, pc);
                }
            }

            // Finally, display the new Bitmap to the user:
            setImageBitmap(mWorkingBitmap);
        }
        
        private void writeString(Canvas canvas, PostCardObject hl) {
            int x = 0, y = 0;
            final ArrayList<String> s = (ArrayList<String>)hl.object;
            final Paint p = hl.paint;
            if (s != null && !s.isEmpty()) {
                x = hl.x;
                y = hl.y;
//                Log.i(TAG, " - x =" + x + ", y = " + y);

                final int spacing = ( (int)p.getTextSize() - 13) + 13;
//                final int spacing = 13;
                int y1 = y - spacing, cw = 0, ch = 0;
                Rect r = new Rect();
                for(String ss : s) {
                    y1 += spacing;
                    p.getTextBounds(ss, 0, ss.length(), r);
                    if(r.width() > cw)
                        cw = r.width();
                    
                    ch += r.height();
                    canvas.drawText(ss, x, y1, p);
                }
                r = null;
                // Stash away bounding boxes for the captions if this
                // is our first time rendering them.
                // Watch out: the x/y position we use for drawing the text is
                // actually the *lower* left corner of the bounding box...
                if (hl.boundingBox == null) {
                    hl.boundingBox = new Rect(x, y == y1 ? (y1 - ch) : (y1 - ch) - 30, x + cw, y1);
                }
            }
        }
        
        private void writeBitmap(Canvas canvas, PostCardObject hl) {
            int x = 0, y = 0;
            final Bitmap b = (Bitmap)hl.object;
            final Paint p = hl.paint;
            if (b != null) {
                x = hl.x;
                y = hl.y;
//                Log.i(TAG, " - x =" + x + ", y = " + y);
                canvas.drawBitmap(b, x, y, p);
                
                y+= 40; //TODO: rect wasn't being draw correctly on the bitmap
                if (hl.boundingBox == null) {
                    hl.boundingBox = new Rect(x, y - b.getHeight(), x + b.getWidth(), y);
                }
            }
        }

        @Override
        protected void onDraw(Canvas canvas) {
//            Log.i(TAG, "onDraw: " + canvas);
            super.onDraw(canvas);

            if (mDragging) {
//                Log.i(TAG, "- dragging!  Drawing box at " + mCurrentDragBox);

                Matrix m = getImageMatrix();

                mCurrentDragBoxF.set(mCurrentDragBox);
                m.mapRect(mTransformedDragBoxF, mCurrentDragBoxF);
                mTransformedDragBoxF.offset(getPaddingLeft(), getPaddingTop());

                Paint p = new Paint();
                p.setColor(Color.BLACK);//RF
                p.setStyle(Paint.Style.STROKE);
                p.setStrokeWidth(2f);
//                Log.i(TAG, "- Paint: " + p);

                canvas.drawRect(mTransformedDragBoxF, p);
            }
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
//            Log.i(TAG, "onTouchEvent: " + ev);

            // Watch out: ev.getX() and ev.getY() are in the
            // coordinate system of the entire LolcatView, although
            // all the positions and rects we use here (like
            // mCaptions[].captionBoundingBox) are relative to the bitmap
            // that's drawn inside the View.
            //
            // To transform between coordinate systems we need to apply the
            // transformation described by the ImageView's matrix *and* also
            // account for our left and top padding.

            Matrix m = getImageMatrix();

            Matrix invertedMatrix = new Matrix();
            m.invert(invertedMatrix);

            float[] pointArray = new float[] { ev.getX() - getPaddingLeft(),
                                               ev.getY() - getPaddingTop() };
//            Log.i(TAG, "  - BEFORE: pointArray = " + pointArray[0] + ", " + pointArray[1]);

            // Transform the X/Y position of the DOWN event back into bitmap coords
            invertedMatrix.mapPoints(pointArray);
//            Log.i(TAG, "  - AFTER:  pointArray = " + pointArray[0] + ", " + pointArray[1]);

            int eventX = (int) pointArray[0];
            int eventY = (int) pointArray[1];

            int action = ev.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    if (mDragging) {
//                        Log.w(TAG, "Got an ACTION_DOWN, but we were already dragging!");
                        mDragging = false;  // and continue as if we weren't already dragging...
                    }
                    if (!hasValidObjects()) {
//                        Log.w(TAG, "No caption(s) yet; ignoring this ACTION_DOWN event.");
                        return true;
                    }
                    
                    // See if this DOWN event hit one of the caption bounding
                    // boxes.  If so, start dragging!
                    
                    final ArrayList<PostCardObject> list = availableObjects;
                    final int size = list.size();
                    for (int i = 0; i < size; i++) {
                        PostCardObject pc = list.get(i);
                        Rect boundingBox = pc.boundingBox;
//                        Log.i(TAG, "  - boundingBox #" + i + ": " + boundingBox + "...");

                        if (boundingBox != null) {
                            // Expand the bounding box by a fudge factor to make it
                            // easier to hit (since touch accuracy is pretty poor on a
                            // real device, and the captions are fairly small...)
                            Rect r = new Rect();
                            r.set(boundingBox);

                            final int touchPositionSlop = 40;  // pixels
                            r.inset(-touchPositionSlop, -touchPositionSlop);

//                            Log.i(TAG, "  - Checking expanded bounding box #" + i + ": " + r + "...");
                            if (r.contains(eventX, eventY)) {
//                                Log.i(TAG, "    - Hit! " + pc);
                                mDragging = true;
                                
                                mTouchDownX = eventX;
                                mTouchDownY = eventY;

                                mInitialDragBox.set(boundingBox);
                                mCurrentDragBox.set(boundingBox);
                                
                                pcBeingDragged = pc;
                                break;
                            }
                            r = null;
                        }
                    }

                    if (!mDragging) {
//                        Log.i(TAG, "- ACTION_DOWN event didn't hit any captions; ignoring.");
                        return true;
                    }

                    invalidate();

                    return true;

                case MotionEvent.ACTION_MOVE:
                    if (!mDragging) {
                        return true;
                    }

                    int displacementX = eventX - mTouchDownX;
                    int displacementY = eventY - mTouchDownY;

                    mCurrentDragBox.set(mInitialDragBox);
                    mCurrentDragBox.offset(displacementX, displacementY);

                    invalidate();

                    return true;

                case MotionEvent.ACTION_UP:
                    if (!mDragging) {
                        return true;
                    }

                    mDragging = false;
                    
                    final PostCardObject pc = pcBeingDragged;

                    int offsetX = eventX - mTouchDownX;
                    int offsetY = eventY - mTouchDownY;
//                    Log.i(TAG, "  - OFFSET: " + offsetX + ", " + offsetY);
                    
                    // Reposition the the caption we just dragged, and blow
                    // away the cached bounding box to make sure it'll get
                    // recomputed in renderCaptions().
                    pc.x += offsetX;
                    pc.y += offsetY;
                    pc.boundingBox = null;

                    //this means change of something
                    if(offsetX <= 1 && offsetY <= 1) {
                        change(pc);
//                        return true;
                    }

                    // Finally, refresh the screen.
                    renderScreen();
                    return true;

                // This case isn't expected to happen.
                case MotionEvent.ACTION_CANCEL:
                    if (!mDragging) {
                        return true;
                    }

                    mDragging = false;
                    // Refresh the screen.
                    renderScreen();
                    return true;

                default:
                    return super.onTouchEvent(ev);
            }
        }
    }

    class PostCardObject {
        public int x, y;
        public Rect boundingBox;
        public Object object;
        public Paint paint;
    }
}
